package river_city_ransom.contracts;

import java.util.ArrayList;
import java.util.List;

import exceptions.ComposantError;
import exceptions.InvariantError;
import exceptions.PostconditionError;
import exceptions.PreconditionError;
import river_city_ransom.decorators.Terrain_D;
import river_city_ransom.services.Bloc;
import river_city_ransom.services.Dimensions;
import river_city_ransom.services.Objet;
import river_city_ransom.services.Position;
import river_city_ransom.services.Terrain;
import tools.Toolbox;

public class Terrain_C extends Terrain_D {
	public Terrain_C(Terrain delegate) {
		super(delegate);
	}
	
	/** _________________________________________________________ OBSERVATORS */
	@Override
	public Dimensions dimensions() {
		return super.dimensions();
	}

	@Override
	public List<Bloc> blocs() {
		return super.blocs();
	}

	@Override
	public Bloc bloc(Position p) throws ComposantError {
		Bloc b = super.bloc(p);
		
		boolean inv;
//		inv bloc(T, p) = { b si ∃b ∈ blocs(T), Bloc::position(b) = p
//						 { NULL sinon
		for(Bloc b_tmp : blocs()) {
			if(Toolbox.equals(b_tmp.position(), p)) {
				inv = Toolbox.equals(b_tmp, b);
				if(!inv) {
					throw new InvariantError("∃b ∈ blocs(T), Bloc::position(b) = p -> bloc(T, p) = b");
				}
				return b;
			}
		}
		inv = b == null;
		if(!inv) {
			throw new InvariantError("¬(∃b ∈ blocs(T)), Bloc::position(b) = p -> bloc(T, p) = NULL");
		}
		
		return b;
	}

	@Override
	public List<Objet> objets() {
		return super.objets();
	}
	
	/** ________________________________________________________ CONSTRUCTORS */
	public void init(Dimensions d, List<Bloc> b, List<Objet> lo) throws ComposantError {
		boolean pre;
//		pre init(d, b, lo) require d != NULL ^ b != Ø ^ lo != Ø
		pre = d != null && b.size() != 0 && lo.size() != 0;
		if(!pre) {
			throw new PreconditionError("init(d, b, lo) require d != NULL ^ b != Ø ^ lo != Ø");
		}
		
		super.init(d, b, lo);
		
		boolean post;
//		post dimensions(init(d, b, lo)) = d
		post = Toolbox.equals(dimensions(), d);
		if(!post) {
			throw new PostconditionError("post position(init(bt, p)) = p");
		}
//		post blocs(init(d, b, lo)) = b
		post = Toolbox.equals(blocs(), b);
		if(!post) {
			throw new PostconditionError("post blocs(init(d, b, lo)) = b");
		}
//		post objets(init(d, b, lo)) = lo
		post = Toolbox.equals(objets(), lo);
		if(!post) {
			throw new PostconditionError("post objets(init(d, b, lo)) = lo");
		}
	}
	
	
	/** ___________________________________________________________ OPERATORS */
	public void setBloc(Bloc bloc) throws ComposantError {
		boolean pre;
//		pre setBloc(T, bloc) require bloc != NULL ^ bloc(T, bloc) = NULL
		pre = bloc != null && bloc(bloc.position()) == null;
		if(!pre) {
			throw new PreconditionError("setBloc(T, bloc) require bloc != NULL ^ bloc(T, bloc) = NULL");
		}
		
		List<Objet> objets_pre = new ArrayList<Objet>(objets());
		List<Bloc> blocs_pre = new ArrayList<Bloc>(blocs());
		
		super.setBloc(bloc);
		
		boolean post;
//		post blocs(setBloc(T, b)) = blocs(T) U {b}
		blocs_pre.add(bloc);
		post = Toolbox.equals(blocs(), blocs_pre);
		if(!post) {
			throw new PostconditionError("post blocs(setBloc(T, b)) = blocs(T) U {b}");
		}
//		post objets(setBloc(T, b)) = objets(T)
		post = Toolbox.equals(objets(), objets_pre);
		if(!post) {
			throw new PostconditionError("post objets(setBloc(T, b)) = objets(T)");
		}
	}
	
	
	public void delBloc(Bloc bloc) throws ComposantError {
		boolean pre;
		//TODO; signe
//		pre delBloc(T, bloc) require bloc != NULL ^ bloc appartient blocs(T)
		pre = bloc != null && blocs().contains(bloc);
		if(!pre) {
			throw new PreconditionError("delBloc(T, bloc) require bloc != NULL ^ bloc appartient blocs(T)");
		}
		
		List<Bloc> blocs_pre = new ArrayList<Bloc>(blocs());
		List<Objet> objets_pre = new ArrayList<Objet>(objets());
		
		super.delBloc(bloc);
		
		boolean post;
//		post blocs(delBloc(T, b)) = blocs(T) \ {b}
		blocs_pre.remove(bloc);
		post = Toolbox.equals(blocs(), blocs_pre);
		if(!post) {
			throw new PostconditionError("post blocs(delBloc(T, b)) = blocs(T) \\ {b}");
		}
//		post objets(delBloc(T, b)) = objets(T)
		post = Toolbox.equals(objets(), objets_pre);
		if(!post) {
			throw new PostconditionError("post objets(delBloc(T, b)) = objets(T)");
		}
	}
	
	public void addObjet(Objet o) throws ComposantError {
		boolean pre;
//		pre addObjet(T, o) require o != NULL
		pre = o != null;
		if(!pre) {
			throw new PreconditionError("addObjet(T, o) require o != NULL");
		}
		
		List<Bloc> blocs_pre = new ArrayList<Bloc>(blocs());
		List<Objet> objets_pre = new ArrayList<Objet>(objets());
		
		super.addObjet(o);
		
		boolean post;
//		post objets(addObjet(T, o)) = objets(T) \ o
		objets_pre.add(o);
		post = Toolbox.equals(objets(), objets_pre);
		if(!post) {
			throw new PostconditionError("post objets(addObjet(T, o)) = objets(T) \\ o");
		}
//		post blocs(addObjet(T,o)) = blocs(T)
		post = Toolbox.equals(blocs(), blocs_pre);
		if(!post) {
			throw new PostconditionError(" post blocs(addObjet(T,o)) = blocs(T)");
		}
	}
	
	public void delObjet(Objet o) throws ComposantError {
		boolean pre;
//		pre delObjet(T, o) require o != NULL ^ o appartient objets(T)
		pre = o != null && objets().contains(o);
		if(!pre) {
			throw new PreconditionError("delObjet(T, o) require o != NULL");
		}
		
		List<Bloc> blocs_pre = new ArrayList<Bloc>(blocs());
		List<Objet> objets_pre = new ArrayList<Objet>(objets());
		
		super.delObjet(o);
		
		boolean post;
//		post objets(delObjet(T, o)) = objets(T) \ o
		objets_pre.remove(o);
		post = Toolbox.equals(objets(), objets_pre);
		if(!post) {
			throw new PostconditionError("post objets(delObjet(T,o)) = objets(T) \\ o");
		}
//		post blocs(delObjet(T, o)) = blocs(T)
		post = Toolbox.equals(blocs(), blocs_pre);
		if(!post) {
			throw new PostconditionError(" post blocs(delObjet(T, o)) = blocs(T)");
		}
	}
}
